//#define _CRT_SECURE_NO_WARNINGS
#include "stdafx.h"
#include <ctime>
#include <assert.h>
#include "debug.h"
//#include "StringObject.h"
#include "DL_StackWalker.h"

namespace Utilities
{
	namespace Debug
	{

		Debug *Debug::ourInstance = 0;
		std::string Debug::DebugLogFilename = "Debug.log";

		Debug *Debug::GetInstance(const OS::FileFlags &someFlags)
		{
			if(ourInstance == 0)
			{
				ourInstance = new Debug(someFlags);
			}
			return ourInstance;
		}

		void Debug::Assert(const DebugLevel &aDebugLevel, const bool &aCondition, const std::string aMessage)
		{
			DL_Debug::StackWalker sw;
			Log(aDebugLevel, aMessage);
			ourInstance->IncreaseTabCount();
			sw.ShowCallstack();
			ourInstance->DecreaseTabCount();
			assert(aCondition);
		}

		void Debug::Assert(const DebugLevel &aDebugLevel, const bool &aCondition, const std::string aFile, const int &aLine, const std::string aFunction, const std::string aMessage)
		{
			DL_Debug::StackWalker sw;
			Log(aDebugLevel, aFile, aLine, aFunction, aMessage);
			ourInstance->IncreaseTabCount();
			ourInstance->Print("Dumping Callstack");
			sw.ShowCallstack();
			ourInstance->DecreaseTabCount();
			assert(aCondition);
		}

		void Debug::Log(const DebugLevel &aDebugLevel, const std::string aMessage)
		{
			GetInstance();
			switch(aDebugLevel)
			{
			case DebugWarning:
				ourInstance->Warning(aMessage);
				break;
			case DebugError:
				ourInstance->Error(aMessage);
				break;
			case DebugSevere:
				ourInstance->Severe(aMessage);
				break;
			case DebugCritical:
				ourInstance->Critical(aMessage);
				break;
			default:
				ourInstance->Print(aMessage);
				break;
			}
		}

		void Debug::Log(const DebugLevel &aDebugLevel, const std::string aFile, const int &aLine, const std::string aFunction, const std::string aMessage)
		{
			char line[255];
			_itoa_s(aLine, &line[0], 255, 10);
			Log(aDebugLevel, (aMessage + "\n\t" + std::string("File: '") + aFile + "' Line: " + line + " in function " + aFunction).c_str());
		}

		void Debug::SetDebugLevel(const DebugLevel &aDebugLevel)
		{
			GetInstance()->myDebugLevel = aDebugLevel;
		}		
		
		void Debug::IncreaseTabCount()
		{
			myTabCount++;
		}

		void Debug::DecreaseTabCount()
		{
			assert(myTabCount != 0);
			myTabCount--;
		}

		Debug::Debug(const OS::FileFlags &someFlags)
		{
			std::string formatMessage;
			myDebugLevel = DebugAll;
			myTabCount = 0;

			if(myErrLog.Open(DebugLogFilename, someFlags))
			{
				time_t now;
				time(&now);
				char buffer[255];
				tm time;
				localtime_s(&time, &now);
				asctime_s(&buffer[0], 255, &time);
				formatMessage += "--------------------------------------\n";
				formatMessage += "Log started at ";
				//formatMessage += asctime_s(localtime(&now));
				formatMessage += std::string(buffer);
				formatMessage += "\n";
				myErrLog.WriteString(formatMessage.c_str());
				myErrLog.FlushToDisk();
			}
		}

		Debug::~Debug()
		{
			myErrLog.Close();
		}

		void Debug::Print(std::string errorMessage)
		{
			WriteString(errorMessage);
		}

		void Debug::Warning(std::string errorMessage)
		{
			if(myDebugLevel <= DebugWarning)
			{
				std::cerr << "Warning: " << errorMessage << std::endl;
				WriteString("Warning: " + errorMessage);
			}
		}

		void Debug::Error(std::string errorMessage)
		{
			if(myDebugLevel <= DebugError)
			{
				std::cerr << "Error: " << errorMessage << std::endl;
				WriteString("Error: " + errorMessage);
			}
		}

		void Debug::Severe(std::string errorMessage)
		{
			if(myDebugLevel <= DebugSevere)
			{
				std::cerr << "Severe: " << errorMessage << std::endl;
				WriteString("Severe: " + errorMessage);
			}

		}

		void Debug::Critical(std::string errorMessage)
		{
			if(myDebugLevel <= DebugCritical)
			{
				std::cerr << "Critical: " << errorMessage << std::endl;
				WriteString("Critical: " + errorMessage);
			}
		}

		void Debug::WriteString(std::string aMessage)
		{
			std::string tabs;
			for(unsigned int i = 0; i < myTabCount; i++)
			{
				tabs += "\t";
			}

			myErrLog.WriteString(tabs + aMessage + "\n");
			myErrLog.FlushToDisk();
		}


		void Assert(const std::string aFile, const int &aLine, const std::string aFunction, const std::string aMessage)
		{
			Debug::Assert(DebugError, false, aFile, aLine, aFunction, aMessage);
		}

		void Log(const DebugLevel &aDebugLevel, const std::string aMessage)
		{
			Debug::Log(aDebugLevel, aMessage);
		}

		void Log(const DebugLevel &aDebugLevel, const std::string aFile, const int &aLine, const std::string aFunction, const std::string aMessage)
		{
			Debug::Log(aDebugLevel, aFile, aLine, aFunction, aMessage);
		}

// 		void Log(const DebugLevel &aDebugLevel, const std::string aMessage, const double &aDouble)
// 		{
// 			Log(aDebugLevel, (Utils::String(aMessage)<<aDouble).c_str());
// 		}
// 
// 		void Log(const DebugLevel &aDebugLevel, const std::string aFile, const int &aLine, const std::string aFunction, const std::string aMessage, const double &aDouble)
// 		{
// 			Log(aDebugLevel, aFile, aLine, aFunction, (Utils::String(aMessage)<<aDouble).c_str());
// 		}

		void SetDebugLevel(const DebugLevel &aDebugLevel)
		{
			Debug::GetInstance()->SetDebugLevel(aDebugLevel);
		}
	};
};
namespace Utils = Utilities;

